package com.exmple.demo.study;

/**
 * 多线程情况下只创建一个对象
 * @author zhangfc
 * @date 2022/1/20
 */
public class SingoTemple {

    /**
     * 防止出现指令重排的情况
     * <p>volatile</p> 禁止指令重排
     * <if>不加volatile关键字</if>
     * <then>出现instance = null的情景</then>
     * <because>getInstance()会在机器中编译为三条指令
     * 1.memory = allocate(); 分配对象内存空间
     * 2.instance(memory); 初始化对象
     * 3.instance = memory; 设置instance指向刚分配的内存地址
     * <ex>
     *     因为步骤2和步骤3不存在数据依赖关系,而且无论是重排前还是重排后程序执行结果在单线程中并没有改变,因此这种重排优化
     *     是允许的.
     *     1.memory = allocate();分配对象内存空间
     *     2.instance = memory;设置instance 指向刚分配的内存地址 此时instance != null,但是对象初始化还没完成
     *     3.instance(memory); 初始化对象
     *     以上特例的instance != null 因此会造成线程安全但并未初始化的问题.
     *     使用volatile会防止指令重排的问题
     * </ex>
     * </because>
     */
    private static volatile SingoTemple instance = null;

    private SingoTemple() {
        System.out.println(Thread.currentThread().getName() + "线程创建一个对象");
    }

    private static SingoTemple getInstance() {
        if (instance == null) {
            synchronized (SingoTemple.class) {
                if (instance == null) {
                    instance = new SingoTemple();
                }
            }
        }
        return instance;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(SingoTemple::getInstance).start();
        }
    }
}
